module FdArray where

import FdType
import FdUtil
import FdHelper

import Data.List
import Data.IORef
import Monad
import Control.Monad.Error
import System.IO.Unsafe(unsafePerformIO)

fd_new_array :: [Val] -> Fineday Val
fd_new_array vals = fmap Array $ makeArray vals

fd_nil_array = fd_new_array []

fd_get_array_elem :: [Val] -> Fineday Val
fd_get_array_elem [Array arr, Number i] = do {
    res <- getArrayItem arr (round i);
    return (case res of 
        Just elem -> elem
        Nothing   -> Nil);
}
fd_get_array_elem paras = throwError $ ParamErr (length paras) 2

fd_set_array_elem :: [Val] -> Fineday Val 
fd_set_array_elem [Array arr, Number i, val] = setArrayItem arr (round i) val >> return val
fd_set_array_elem [_, _, _]                  = throwError $ TypeErr $ ""
fd_set_array_elem paras                      = throwError $ ParamErr (length paras) 3

fd_copy_array [Array arr] = fmap Array $ copyArray arr
fd_copy_array [_]         = throwError $ TypeErr $ "not Array"
fd_copy_array paras       = throwError $ ParamErr (length paras) 1

fd_concat_array [Array arr1, Array arr2] = fmap Array $ concatArray arr1 arr2
fd_concat_array [_, _]                   = throwError $ TypeErr $ "not Array"
fd_concat_array paras                    = throwError $ ParamErr (length paras) 2

-- type Arr = IORef [IORef Val]
--

makeArray :: [a] -> Fineday (ArrayRef a)
makeArray vals = do {
	valsM <- mapM (io . newIORef) vals;
	io $ newIORef valsM;
}

toList :: ArrayRef a -> Fineday [a]
toList arrRef = do {
	arrM <- io $ readIORef arrRef;
	arr <- mapM (io . readIORef) arrM;
	return arr;
}

getArrayItem :: ArrayRef a -> Int -> Fineday (Maybe a)
getArrayItem arrRef index = do {
	arr <- io $ readIORef arrRef;
	if (index+1) > (length arr) then
		return $ Nothing
	else do {
		io $ fmap Just $ readIORef (arr !! index);
	}
}

setArrayItem :: ArrayRef a -> Int -> a -> Fineday a
setArrayItem arrRef index val = do {
	arr <- io $ readIORef arrRef;
	io $ writeIORef (arr !! index) val;
	return val;
}

appendArrayItem :: ArrayRef a -> a -> Fineday (ArrayRef a)
appendArrayItem arrRef val = do {
	newElem <- io $ newIORef val;
	io $ modifyIORef arrRef (\arr ->
		arr ++ [newElem]
	);
	return $ arrRef;
}

copyArray :: ArrayRef a -> Fineday (ArrayRef a)
copyArray arrRef = do {
    list <- toList arrRef;
    makeArray $ list;
}

concatArray :: ArrayRef a -> ArrayRef a -> Fineday (ArrayRef a)
concatArray arrRef1 arrRef2 = do {
    newArr <- copyArray arrRef2;
    list <- io $ readIORef newArr;
    io $ modifyIORef arrRef1 (\arr -> 
        arr ++ list
    );
    return arrRef1;
}

removeArrayItem :: ArrayRef a -> Int -> Fineday (ArrayRef a)
removeArrayItem arrRef index = do {
	io $ modifyIORef arrRef (\arr ->
			let (head, _:tail) = splitAt index arr
			in  head ++ tail
	);
	return $ arrRef; 
}

-- 

